extends Node2D
class_name Chessboard
##我们在此规定Chessboard类的作用仅限于提供地块格数据和状态，不参与与棋子移动攻击有关任何运算。

const CHESSBOARD_CHESS_GROUP:String = "ChessOnChessboard"

@onready var baselayer: TileMapLayer = $BaseLayer
@onready var walllayer: TileMapLayer = $WallLayer

var astar:TileAstar = TileAstar.new()

var layers:Array[TileMapLayer] = []

var solid_cells:Array[Vector2i] = []
var chessboard_cells:Array[Vector2i] = []

var mouse_cell:Vector2i:
	get():
		return _get_mouse_cell()

func _ready() -> void:
	for temp_node in get_children():
		if temp_node is TileMapLayer:
			layers.append(temp_node)
	var cells_cost:Dictionary = _get_cells_cost()
	astar.initialize(baselayer,solid_cells,cells_cost)

#region 路径相关
##获取起点所在单元格到终点所在单元格的路径
func get_astar_path(start_pos:Vector2,end_pos:Vector2)->Array[Vector2]:
	var path:Array[Vector2]
	var start_cell = local_to_map(start_pos)
	var end_cell = local_to_map(end_pos)
	if has_cell(start_cell) and has_cell(end_cell):
		var tile_path = astar.get_path_from_to(start_cell,end_cell,solid_cells)
		for cell in tile_path:
			path.append(map_to_local(cell))
	return path
##获取起点所在单元格到鼠标所在单元格的路径
func get_astar_path_to_mouse(start_pos:Vector2)->Array[Vector2]:
	var start_cell = local_to_map(start_pos)
	var path:Array[Vector2]
	if has_cell(mouse_cell):
		var tile_path = astar.get_path_from_to(start_cell,mouse_cell,solid_cells)
		for cell in tile_path:
			path.append(map_to_local(cell))
	return path
#endregion

#region 获取单元格数据
##获取单元格特定数据
func _get_cell_data(layer:TileMapLayer,cell:Vector2i,data_name:String)->Variant:
	var tiledata = layer.get_cell_tile_data(cell)
	if !tiledata:
		return null
	if !tiledata.has_custom_data(data_name):
		return null
	var data
	data = tiledata.get_custom_data(data_name)
	return data
##获取格子的移动力
func _get_cells_cost()->Dictionary:
	var cells_cost:Dictionary
	for layer in layers:
		for cell in layer.get_used_cells():
			var cell_cost = _get_cell_data(layer,cell,"cost")
			if !cell_cost:
				continue
			if !cells_cost.has(cell):
				cells_cost[cell] = cell_cost
			else :
				cells_cost[cell] += cell_cost
	return cells_cost
#endregion

#region 坐标转换相关方法
func local_to_map(_pos:Vector2)->Vector2i:
	return baselayer.local_to_map(_pos)
func map_to_local(_cell:Vector2i)->Vector2:
	return baselayer.map_to_local(_cell)
##该方法能够将全局坐标转化为对应单元格中心的本地坐标
func local_to_center(_cell:Vector2)->Vector2:
	return map_to_local(local_to_map(_cell))
func local_path_to_map_path(path:Array[Vector2])->Array[Vector2i]:
	var new_path:Array[Vector2i]
	for temp_pos in path:
		var new_cell = baselayer.local_to_map(temp_pos)
		new_path.append(new_cell)
	return new_path
func map_path_to_local_path(path:Array[Vector2i])->Array[Vector2]:
	var new_path:Array[Vector2]
	for temp_pos in path:
		var new_cell = baselayer.map_to_local(temp_pos)
		new_path.append(new_cell)
	return new_path
#endregion


#region 判断
##判断地块是否存在于地图上
func has_cell(_cell: Vector2i) -> bool:
	return baselayer.get_used_cells().has(_cell)
##判断两个全局坐标点是否在同一单元格中
func is_in_same_cell(position1:Vector2,position2:Vector2):
	return baselayer.local_to_map(position1) == baselayer.local_to_map(position2)
func _is_solid_cell(cell:Vector2i):
	var is_solid_cell = solid_cells.has(cell)
	return is_solid_cell
#endregion

#region 判断鼠标是否在目标范围内
##判断鼠标是否在单个目标单元格
func is_mouse_in_cell(center_cell:Vector2i)->bool:
	return center_cell == mouse_cell
##判断鼠标是否在非几何图形区域中
func is_mouse_in_cells(cells:Array[Vector2i])->bool:
	for cell in cells:
		if is_mouse_in_cell(cell):
			return true
	return false
#endregion

#region 获取单元格
##获取鼠标所在单元格
func _get_mouse_cell()->Vector2i:
	return local_to_map(get_global_mouse_position())

func _get_group_node_cells(group_name:String)->Array[Vector2i]:
	var cells:Array[Vector2i]
	for node in get_tree().get_nodes_in_group(group_name):
		cells.append(local_to_map(node.position))
	return cells

func get_chessboard_cells()->Array[Vector2i]:
	_refresh_chessboard_cells()
	return chessboard_cells
func _refresh_chessboard_cells():
	chessboard_cells.clear()
	for layer in layers:
		var temp_layer_cells:Array[Vector2i] = layer.get_used_cells()
		for temp_cell in temp_layer_cells:
			if !chessboard_cells.has(temp_cell):
				chessboard_cells.append(temp_cell)

func get_solid_cells()->Array[Vector2i]:
	_refresh_solid_cells()
	return solid_cells
func _refresh_solid_cells():
	var new_entity_cells:Array[Vector2i] = _get_group_node_cells("ChessOnChessboard")
	var new_wall_cells:Array[Vector2i] = walllayer.get_used_cells()
	solid_cells.clear()
	solid_cells.append_array(new_entity_cells)
	solid_cells.append_array(new_wall_cells)

func get_unsolid_cells()->Array[Vector2i]:
	var new_unsolid_cells:Array[Vector2i] = filter_cells_to_unsolid_cells(chessboard_cells)
	return new_unsolid_cells

func get_straight_line_path_from_cell(start_cell:Vector2i,dir:Vector2i,longth:int)->Array[Vector2i]:
	var new_line_cells:Array[Vector2i] = ShapeCells.get_line_cells(start_cell,dir,longth)
	##为什么这里要擦除起点？因为这个方法经常被用于请求实体的直线路径。所以起点格有极高概率是实体所在的格子。
	##那么如果不擦除起点，过滤方法filter_line_cells_into_blocked_cells就会返回空集。
	new_line_cells.erase(start_cell)
	var new_blocked_cells = filter_line_cells_into_blocked_cells(new_line_cells)
	new_blocked_cells.push_front(start_cell)
	return new_blocked_cells

func get_line_path_with_dirs(start_cell:Vector2i,dirs:Array[Vector2i])->Array[Vector2i]:
	var path:Array[Vector2i] = []
	var new_cell:Vector2i = start_cell
	for dir in dirs:
		new_cell += dir
		path.append(new_cell)
	var new_path = filter_line_cells_into_blocked_cells(path)
	new_path.push_front(start_cell)
	return new_path
#endregion

#region 过滤器
func filter_cells_to_unsolid_cells(cells:Array[Vector2i])->Array[Vector2i]:
	_refresh_solid_cells()
	var new_cells:Array[Vector2i]
	for temp_cell in cells:
		if !solid_cells.has(temp_cell):
			new_cells.append(temp_cell)
	return new_cells
func filter_cells_into_chessboard_cells(cells:Array[Vector2i])->Array[Vector2i]:
	_refresh_chessboard_cells()
	var new_cells:Array[Vector2i]
	for temp_cell in cells:
		if chessboard_cells.has(temp_cell):
			new_cells.append(temp_cell)
	return new_cells
func filter_line_cells_into_blocked_cells(line_cells:Array[Vector2i])->Array[Vector2i]:
	_refresh_solid_cells()
	var blocked_cell_pos:int = line_cells.find_custom(_is_solid_cell.bind())
	var new_blocked_solid_cells:Array[Vector2i]
	if blocked_cell_pos == -1:
		return line_cells
	new_blocked_solid_cells = line_cells.slice(0,blocked_cell_pos)
	return new_blocked_solid_cells
#endregion
